<template>
  <ADropdown trigger="click" :arrow="{ pointAtCenter: true }" v-model:open="visible">
    <div class="r-color-picker-trigger">
      <div class="color-picker-inner" :style="{ '--color': formatRgbColor(colorValue) }" :data-format="config.format">
        <div class="color-picker-inner__text">{{ colorValue }}</div>
      </div>
    </div>

    <template #overlay>
      <div class="color-picker-container">
        <div class="color-panel-container">
          <div
            class="color-panel-inner"
            ref="targetRef"
            :style="{ '--color': panelColor }"
            @click="(e) => onDistanceUpdate({ left: e.offsetX, top: e.offsetY })"
          ></div>
          <div ref="dotRef" class="color-panel-handle" v-drag="{ onMoveChange: onDistanceChange }"></div>
        </div>
        <AFormItemRest>
          <div class="color-picker-slider">
            <RColorSlider v-model:value="panelColor" @change="(v) => computerColor(v, bounds)" />
          </div>
          <ASpace style="width: 100%">
            <ASelect
              style="width: 72px"
              v-model:value="config.format"
              :options="options"
              size="small"
              :disabled="props.simple"
              @change="() => updateValue(colorValue)"
            ></ASelect>
            <RColorInput :format="config.format" size="small" :value="colorValue" @change="onInputChange" />
          </ASpace>
        </AFormItemRest>
      </div>
    </template>
  </ADropdown>
</template>

<script setup>
import { ref, reactive, toValue, onMounted, watch, nextTick } from 'vue';
import RColorSlider from './modules/RColorSlider.vue';
import RColorInput from './modules/RColorInput.vue';
import { useState } from '@/hooks/index.js';
import { formatColor, formatRgbColor, hsvToRgbArray, rgbToHsv, rgbToHue } from '@/utils/color/index.js';

defineOptions({
  inheritAttrs: false
});

const props = defineProps({
  defaultValue: { type: String, default: '#1677FF' }, // 默认值
  format: { type: String, default: 'hex' },
  simple: { type: Boolean, default: false }
});

const options = [
  { value: 'hex', label: 'HEX' },
  { value: 'hsb', label: 'HSB' },
  { value: 'rgb', label: 'RGB' }
];
const emit = defineEmits(['update:value']);
const colorValue = defineModel('value', { type: String, default: '' });
const visible = ref(false);

const targetRef = ref(null);
const dotRef = ref(null);
const [panelColor, setPanelColor] = useState('rgb(0, 106, 255)');
const [bounds, setBounds] = useState({ left: 0, top: 0, maxLeft: 0, maxTop: 0 });

const config = reactive({ format: props.format, alpha: 100 });

/**
 * 更新颜色
 *
 * @type {(function(): void)|*}
 */
const updateValue = (val) => {
  const color = formatColor(val, config.format);
  emit('update:value', color);
};

onMounted(() => {
  const rgb = formatRgbColor(toValue(colorValue) || props.defaultValue);
  updateValue(rgb);
});

watch(visible, (val) => {
  if (val) {
    nextTick(() => setTimeout(() => initValue(), 0));
  }
});

function initValue() {
  const { offsetWidth: maxLeft, offsetHeight: maxTop } = toValue(targetRef);
  handleColorChange(toValue(colorValue), { maxLeft, maxTop });
}

/**
 * 初始化颜色
 *
 * @param {string} color
 * @param {Object} bounds
 */
const handleColorChange = (color, bounds) => {
  const rgbColor = formatRgbColor(color);
  const [_hue, _saturation, _value] = rgbToHsv(rgbColor);
  const panelRgbColor = formatRgbColor(toValue(panelColor));
  const panelHue = rgbToHue(panelRgbColor);

  // 判断颜色是否在当前面板
  if (_hue !== panelHue) {
    // 获取颜色所在面板的颜色
    const rgbArray = hsvToRgbArray(_hue, 1, 1);
    setPanelColor(`rgb(${rgbArray.join(', ')})`);
  }

  const { maxLeft, maxTop } = bounds;
  // 设置距离
  const left = Math.round((_saturation / 100) * maxLeft);
  const top = Math.round(((100 - _value) / 100) * maxTop);
  onDistanceUpdate({ left, top, maxLeft, maxTop });
};

/**
 * 设置距离
 */
const onDistanceUpdate = (bounds) => {
  toValue(dotRef)?.updateBounds?.(bounds);
};

/**
 * 位置变化
 */
const onDistanceChange = (bounds) => {
  computerColor(toValue(panelColor), bounds);
  setBounds(bounds);
};

/**
 * 计算颜色
 *
 * @param color 面板颜色
 * @param bounds 位置信息
 */
const computerColor = (color, bounds) => {
  const { left, top, maxLeft, maxTop } = bounds;
  const _saturation = Math.round((left / maxLeft) * 100) / 100;
  const _value = Math.round((1 - top / maxTop) * 100) / 100;
  const _hue = rgbToHue(toValue(color));
  const rgbArray = hsvToRgbArray(_hue, _saturation, _value);
  const rgb = `rgb(${rgbArray.join(', ')})`;
  toValue(colorValue) === rgb || updateValue(rgb);
};

const onInputChange = (val) => {
  updateValue(val);
  handleColorChange(val, toValue(bounds));
};
</script>

<style lang="scss" scoped>
.r-color-picker-trigger {
  width: auto;
  height: auto;
  padding: 4px;
  color: var(--text-color);
  background-color: var(--bg-color);
  border: 1px solid var(--border-color);
  border-radius: var(--border-radius);
  cursor: pointer;
  display: inline-block;

  .color-picker-inner {
    width: 72px;
    height: 100%;
    padding: 0 8px;
    background-color: var(--color);
    text-align: center;

    &__text {
      font-size: 14px;
      color: #ffffff;
      font-weight: 400;
      mix-blend-mode: difference;
    }

    &[data-format='rgb'] {
      width: 120px;
    }
    &[data-format='hsb'] {
      width: 140px;
    }
  }
}

.color-picker-container {
  width: auto;
  height: auto;
  background-color: var(--bg-color);
  border-radius: var(--border-radius);
  box-shadow: 0 0 4px var(--box-shadow-color);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  align-items: center;
}

.color-panel-container {
  width: 280px;
  height: 180px;
  position: relative;
  user-select: none;

  .color-panel-inner {
    width: 100%;
    height: 100%;
    border-radius: var(--border-radius);
    background-color: var(--color);
    background-image: linear-gradient(0deg, rgb(0, 0, 0), transparent),
      linear-gradient(90deg, rgb(255, 255, 255), rgba(255, 255, 255, 0));
  }

  .color-panel-handle {
    --width: 16px;
    cursor: pointer;
    width: var(--width);
    height: var(--width);
    border-radius: 50%;
    border: 2px solid #fff;
    z-index: 1;
    background-color: transparent;
    transform: translate(-50%, -50%);
  }
}
</style>
